//****************************************Copyright (c)***********************************//
//原子哥在线教学平台：www.yuanzige.com
//技术支持：www.openedv.com
//淘宝店铺：http://openedv.taobao.com 
//关注微信公众平台微信号："正点原子"，免费获取ZYNQ & FPGA & STM32 & LINUX资料。
//版权所有，盗版必究。
//Copyright(C) 正点原子 2018-2028
//All rights reserved	                               
//----------------------------------------------------------------------------------------
// File name:           state_data
// Last modified Date:  2022/7/11 14:25:36
// Last Version:        V1.1
// Descriptions:        uart_demo状态机模块
//----------------------------------------------------------------------------------------
// Created by:          正点原子
// Created date:        2022/7/11 9:30:36
// Version:             V1.0
// Descriptions:        The original version
//
//----------------------------------------------------------------------------------------
//****************************************************************************************//

module state_data(
	input						sys_clk,        //系统时钟
	input						sys_rst_n,      //复位，低电平有效
    
	input		[7:0]	        rxd_data,       //发送端的数据
	input						rx_done,        //接收完成信号
	
	output	reg					fsm_done,       //状态机完成信号
	output	reg	[3:0]			state_cnt       //状态计数器
);

//parameter define
parameter	IDLE     =  5'b00001;               //初始状态IDLE
parameter	STATE_H  =  5'b00010;               //字符h 8'd104
parameter	STATE_E  =  5'b00100;               //字符e 8'd101
parameter	STATE_L  =  5'b01000;               //字符l 8'd108
parameter	STATE_P  =  5'b10000;               //字符p 8'd112 
    
//reg define						    
reg				[4:0]			state;          //数据状态
reg				[3:0]			temp_cnt1;      //临时寄存器1
reg				[3:0]			temp_cnt2;      //临时寄存器2
reg								clk_flag;       //状态机分频时钟标志位
reg								cnt_clear;      //计数器清空寄存器
reg				[25:0]		    fsm_clk;        //状态机分频时钟
reg								rx_done_d1;     //接收完成寄存器1
    
//wire define   
wire							rxd_en;         //接收完成信号

//*****************************************************
//**                    main code
//*****************************************************
//抓取一个上升沿作为信号接收的信号
assign rxd_en = rx_done & (~rx_done_d1);

//延迟一个周期用于做完成发送信号
always @ (posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		rx_done_d1 <= 1'd0;
	else
		rx_done_d1 <= rx_done;
end

//状态机的检测状态
always @ (posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		state <= IDLE;
	else begin
		case(state)
			IDLE : begin
				if(rxd_data == 8'd104 && rxd_en)                //接收的是数据h，且接收信号有效
					state <= STATE_H;                          
				else if(rxd_data != 8'd104 && rxd_en)           //数据不是h，且接收信号有效
				    state <= IDLE;
				else 
					state <= IDLE;                             //状态结束 
			end
			STATE_H : begin
				if(rxd_data == 8'd101 && rxd_en)                //接收的是数据e，且接收信号有效
					state <= STATE_E;                          
				else if(rxd_data == 8'd104 && rxd_en)           //数据不是h，且接收信号有效
				    state <= STATE_H;                          
				else if(rxd_en)                                 //接收信号有效
					state <= IDLE;                             
				else                                            //状态结束
				    state <= STATE_H;                          
			end
			STATE_E : begin
				if(rxd_data == 8'd108 && rxd_en)                //接收的是数据l，且接收信号有效
					state <= STATE_L;     
				else if(rxd_data == 8'd104 && rxd_en)           //数据是h，且接收信号有效
				    state <= STATE_H;
				else if(rxd_en)                                 //接收信号有效
					state <= IDLE;
				else                                            //状态结束
					state <= STATE_E;
			end
			STATE_L : begin
				if(rxd_data == 8'd112 && rxd_en)                //接收的是数据p，且接收信号有效
					state <= STATE_P;                           
				else if(rxd_data == 8'd104 && rxd_en)           //数据是h，且接收信号有效
				    state <= STATE_H;                           
			    else if(rxd_en)                                 //接收信号有效
					state <= IDLE;                              
				else                                            //状态结束
					state <= STATE_L;
			end
			STATE_P : begin			                            //状态是P时直接跳转到IDLE
					state <= IDLE;
			end                   
		endcase
	end
end

//状态机计数器
always @ (posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		state_cnt <= 0;
    else if(cnt_clear)                                              //清零计数器有效时
		state_cnt <= 4'd0;                                          //进行清零操作
	else if((rxd_data == 8'd112 && rxd_en) && state == STATE_L)     //接收到的数据是p且发送信号有效状态在L
		state_cnt <= state_cnt + 1'b1;                              //对状态机进行加1    
end

//状态机分频出一个10ms的计数器
always @ (posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n) begin
		fsm_clk <= 26'd0;
		clk_flag <= 1'b0;
	end
	else if(fsm_clk == 26'd500_000) begin
		fsm_clk <= 26'd0;
		clk_flag <= ~clk_flag;
	end
	else begin
		fsm_clk <= fsm_clk + 1'b1;
	end
end

//组合逻辑电路
always @ (*) begin
	temp_cnt1 = (state_cnt * 2);                                 //状态计数器的值放入临时寄存器1   
end

//实时锁存状态机的计数值
always @ (posedge clk_flag or negedge sys_rst_n) begin
	if(!sys_rst_n) begin
		fsm_done <= 1'b0;
		temp_cnt2 <= 4'd0;
		cnt_clear <= 1'b0;
	end
	else begin
		if(temp_cnt2 == 4'd0) begin                             //延迟一个时钟周期
            temp_cnt2 <= temp_cnt1;                                   
		end
		else if(temp_cnt2 > 4'd0) begin	                        //如果临时寄存器大于0
            temp_cnt2 <= temp_cnt2 - 1'b1;                      //临时寄存器减1进行清零自锁   
			fsm_done <= ~fsm_done;                              //电平翻转，拉低一个电平进行下一次计数
			if(temp_cnt2 == 4'd1) begin                         //进入缓存自锁清零状态
				cnt_clear <= 1'b1;                             
				if(state_cnt == 4'd0)	
					if(rxd_data != 8'd112 && rxd_en) begin    //发送的数据不是数据p且是有效信号
					    cnt_clear <= 1'b0;                  
						temp_cnt2 <= 4'd0;
					end
					else begin
					    cnt_clear <= 1'b0;
						temp_cnt2 <= 4'd0;
					end
				else
                    cnt_clear <= 1'b1;
            end        
			else
			   cnt_clear <= 1'b1; 
		end
		else if(temp_cnt2 == 4'd0 && fsm_done == 1'b1)
			fsm_done <= 1'b0;
	end
end


endmodule